<?php
namespace app\model;

use app\common\Model;

class Comment extends Model
{
    public function policyId()
    {
        return 'comment';
    }
    
    public function table()
    {
        return 'mycomment';
    }
    
    private static $showTypes = [
        'nest-one' => '叠加一层',
        'nest-multiple' => '叠加多层',
        'indent-one' => '缩进一层',
        'indent-multiple' => '缩进多层',
    ];
    
    private static $defaultShowType = 'nest-multiple';
    
    private static $renderChildrenMethod = 'renderCommentsNestRecursion';
    
    public function getRegion(array $value)
    {
        if (!empty($value['region']))
        {
            return $value['region'];
        }
        else 
        {
            return '未知星球';
        }
    }
    
    public function getNickname(array $value)
    {
        if (!empty($value['nickname']))
        {
            return $value['nickname'];
        }
        else 
        {
            return '匿名用户';
        }
    }
    
    public function getShowTypes()
    {
        return self::$showTypes;
    }
    
    public function getDefaultShowType()
    {
        return self::$defaultShowType;
    }
    
    
    /**
     * 添加评论
     * @param array $data 必须字段：content
     * @return array ['code', 'msg', 'data' => []]
     */
    public function add(array $data)
    {
        //必须字段
        $requiredFields = ['content'];
        $errors = [];
        //数据验证
        foreach ($requiredFields as $field)
        {
            if (!isset($data[$field]))
            {
                $errors[$field] = "$field 字段必须";
            }
            else
            {
                $value = trim($data[$field]);
                if ($value === '')
                {
                    $errors[$field] = "$field 字段不能为空";
                }
            }
        }
        if (!empty($errors))
        {
            return  [1, '参数错误', ['errors' => $errors]];
        }
        
        $insert = [];
        $rootComment = [];
        //parents + children + parent_id + root_id
        if (!empty($data['parent_id']))
        {
            //处理父节点
            $parentComment = $this->getById(['id' => $data['parent_id']]);
            if (empty($parentComment))
            {
                return [1, sprintf("invalid parent_id: %s", $data['parent_id']), $data];
            }
            if (empty($parentComment['parents']))
            {
                $insert['parents'] = $parentComment['id'];
            }
            else 
            {
                $insert['parents'] = sprintf("%s,%s", $parentComment['parents'], $parentComment['id']);
            }
            //处理根节点
            if (!empty($parentComment['root_id']))
            {
                $rootComment = $this->getById(['id' => $parentComment['root_id']]);
                if (empty($rootComment))
                {
                    return [1, sprintf("parent[%s]'s root comment[%s] not exists.", $data['parent_id']), $parentComment['root_id'], $data];
                }
                $insert['root_id'] = $parentComment['root_id'];
            }
            else
            {
                $rootComment = $parentComment;
                $insert['root_id'] = $parentComment['id'];
            }
            
            $insert['parent_id'] = $data['parent_id'];
            $insert['children'] = '';
        }
        else 
        {
            $insert['parents'] = '';
            $insert['children'] = '';
            $insert['parent_id'] = 0;
            $insert['root_id'] = 0;
        }
        
        $insert['content'] = $data['content'];
        $insert['like_count'] = $insert['dislike_count'] = $insert['status'] = 0;
        $insert['nickname'] = !empty($data['nickname']) ? $data['nickname'] : '';
        $insert['create_time'] = $_SERVER['REQUEST_TIME'];
        
        $ipInfo = ip_info($_SERVER['REMOTE_ADDR']);
        if (!empty($ipInfo))
        {
            $insert['ip'] = $_SERVER['REMOTE_ADDR'];
//            $insert['region'] = $ipInfo['region'] . $ipInfo['city'];
            $insert['region'] = sprintf("%s·%s·%s", $ipInfo[0], $ipInfo[1], $ipInfo[2]);
        }
        else
        {
            $insert['ip'] = $insert['region'] = '';
        }
        
        try
        {
            
            $result = $this->transaction(function() use($insert, $rootComment) {
                //插入楼层
                $id = $this->insert($insert);
                $insert['id'] = $id;
                //更新根节点的children
                if (!empty($rootComment))
                {
                    $this->updateById([
                        'id' => $rootComment['id'],
                        'set' => "children = IF(children = '', $id, CONCAT(children, ',$id'))",
                    ]);
                }
                return [0, '添加成功', $insert];
            });
            
            return $result;
        }
        catch (\Exception $e)
        {
            return [1, sprintf('%s in file: %s at line: %s', $e->getMessage(), $e->getFile(), $e->getLine()), $data];
        }
    }
    /**
     * 按叠加样式取数据
     * 
     * @param array $args
     * @return unknown[]
     */
    public function getCommentsNest($args = ['page' => 1, 'size' => 10])
    {
        $defaults = ['page' => 1, 'size' => 10];
        $args = merge_args($defaults, $args);
        if 
        (
            !ctype_digit((string)$args['page']) || 
            !ctype_digit((string)$args['size'])
        )
        {
            throw new \InvalidArgumentException("Invalid page or size.");
        }
        
        $offset = ($args['page'] - 1) * $args['size'];
        $comments = $this->select([
            'order' => 'create_time ASC',
            'limit' => sprintf("%s, %s", $offset, $args['size']),
        ]);
        if (empty($comments))
        {
            return [];
        }
        $commentsParentsIdArr = [];
        foreach ($comments as &$comment)
        {
            if (!empty($comment['parents']))
            {
                $parentsIdArr = explode(',', $comment['parents']);
                $commentsParentsIdArr = array_merge($commentsParentsIdArr, $parentsIdArr);
                $comment['parents'] = $parentsIdArr;
            }
            else
            {
                $comment['parents'] = [];
            }
        }
        if (!empty($commentsParentsIdArr))
        {
            $commentsParents = $this->select([
                'where' => sprintf("id IN (%s)", implode(',', array_unique($commentsParentsIdArr))),
            ]);
            $commentsParents = array_column($commentsParents, null, 'id');
        }
        else 
        {
            $commentsParents = [];
        }
        
//         dd($commentsParents);
        
        return ['comments' => $comments, 'commentsParents' => $commentsParents];
    }
    
    /**
     * 按缩进样式取数据
     *
     * @param array $args
     * @return unknown[]
     */
    public function getCommentsIndent($args = ['page' => 1, 'size' => 10])
    {
        $defaults = ['page' => 1, 'size' => 10];
        $args = merge_args($defaults, $args);
        if
        (
            !ctype_digit((string)$args['page']) || 
            !ctype_digit((string)$args['size'])
        )
        {
            throw new \InvalidArgumentException("Invalid page or size.");
        }
        
        $offset = ($args['page'] - 1) * $args['size'];
        $comments = $this->select([
            'where' => 'root_id = :root_id',
            'bind' => [':root_id' => 0],
            'order' => 'create_time ASC',
            'limit' => sprintf("%s, %s", $offset, $args['size']),
        ]);
        if (empty($comments))
        {
            return [];
        }
        $commentsIdArr = array_column($comments, 'id');
        $commentsChildren = $this->select([
            'where' => sprintf("root_id IN (%s)", implode(',', $commentsIdArr)),
            'order' => 'create_time ASC',
        ]);
        $commentsChildren = array_column($commentsChildren, null, 'id');
        foreach ($comments as &$comment)
        {
            //将主评论也添加到其中
            $commentsChildren[$comment['id']] = $comment;
            if (!empty($comment['children']))
            {
                $comment['children'] = explode(',', $comment['children']);
            }
            else 
            {
                $comment['children'] = [];
            }
        }
        
        return ['comments' => $comments, 'commentsChildren' => $commentsChildren];
    }
    
    /**
     * 递归的渲染引用
     * 
     * @param array $floorIdArr 楼层ID
     * @param array $floorsInfo 所有楼层信息
     */
    public function renderQuoteRecursion(array $floorIdArr, array $floorsInfo)
    {
        $HTML = '<blockquote class="blockquote text-muted">';
        $floorId = array_pop($floorIdArr);
        if (!empty($floorIdArr))
        {
            $HTML .= $this->renderQuoteRecursion($floorIdArr, $floorsInfo);
        }
        $floor = $floorsInfo[$floorId];
        $HTML .= sprintf('<div class="media" data-id="%s"><div class="media-body"><h6 class="mt-0">', $floor['id']);
        $HTML .= sprintf('<small class="comment-meta">%s</small>', $this->getNickname($floor));
        $HTML .= sprintf('<small class="comment-meta">来自[%s]</small>', $this->getRegion($floor));
        $HTML .= sprintf('<small class="comment-meta">发表于[%s]</small>', date('Y-m-d H:i:s', $floor['create_time']));
        $HTML .= sprintf('</h6>%s', $floor['content']);
        $HTML .= '<p class="actions text-right pointer">';
        $HTML .= sprintf('<span class="like vote">支持(%s)</span>', $floor['like_count']);
        $HTML .= sprintf('<span class="dislike vote">反对(%s)</span>', $floor['dislike_count']);
        $HTML .= '<span class="reply">回复</span></p></div></div></blockquote>';
        
        return $HTML;
    }
    
    /**
     * 迭代的渲染引用
     *
     * @param array $floorIdArr 楼层ID
     * @param array $floorsInfo 所有楼层信息
     */
    public function renderQuoteIteration(array $floorIdArr, array $floorsInfo, $doHide = true)
    {
        $HTML = '';
        while (!empty($floorIdArr))
        {
            $floorId = array_shift($floorIdArr);
            $floor = $floorsInfo[$floorId];
            $HTML = '<blockquote class="blockquote text-muted">' . $HTML;
            $HTML .= sprintf('<div class="media" data-id="%s"><div class="media-body"><h6 class="mt-0">', $floor['id']);
            $HTML .= sprintf('<small class="comment-meta">%s</small>', $this->getNickname($floor));
            $HTML .= sprintf('<small class="comment-meta">来自[%s]</small>', $this->getRegion($floor));
            $HTML .= sprintf('<small class="comment-meta">发表于[%s]</small>', date('Y-m-d H:i:s', $floor['create_time']));
            $HTML .= sprintf('</h6>%s', $floor['content']);
            $HTML .= '<p class="actions text-right">';
            $HTML .= sprintf('<span class="like vote">支持(%s)</span>', $floor['like_count']);
            $HTML .= sprintf('<span class="dislike vote">反对(%s)</span>', $floor['dislike_count']);
            $HTML .= '<span class="reply">回复</span></p></div></div></blockquote>';
            $leftNum = count($floorIdArr);
            if ($doHide && $leftNum >= 2)
            {
                $hideHTML = $this->renderQuoteIteration($floorIdArr, $floorsInfo, false);
                
                $HTML = '<blockquote class="blockquote text-muted">' . $HTML;
                $HTML .= sprintf('<p class="pointer" data-hide="%s" onclick="expand(this)">已隐藏%d层重复盖楼[<span class="text-warning">点击展开</span>]</p>', htmlspecialchars($hideHTML), $leftNum);
                $HTML .= '</blockquote>';
                break;
            }
        }
        return $HTML;
    }
    
    public function renderChildren($parentId, array $floorsInfo)
    {
        $HTML = '';
        foreach ($floorsInfo as $floor)
        {
            if ($floor['parent_id'] == $parentId)
            {
                $HTML .= sprintf('<div class="media mt-3" data-id="%s"><a class="d-flex pr-3" href="#"><img src="/img/avatar.svg" alt="Generic placeholder image"></a><div class="media-body"><h6 class="mt-0">', $floor['id']);
                $HTML .= sprintf('<small class="comment-meta">%s</small>', $this->getNickname($floor));
                $HTML .= sprintf('<small class="comment-meta">来自[%s]</small>', $this->getRegion($floor));
                $HTML .= sprintf('<small class="comment-meta">发表于[%s]</small>', date('Y-m-d H:i:s', $floor['create_time']));
                $HTML .= sprintf('</h6>%s', $floor['content']);
                $HTML .= '<p class="actions text-right">';
                $HTML .= sprintf('<span class="like vote">支持(%s)</span>', $floor['like_count']);
                $HTML .= sprintf('<span class="dislike vote">反对(%s)</span>', $floor['dislike_count']);
                $HTML .= '<span class="reply">回复</span></p>';
                
                $HTML .= $this->renderChildren($floor['id'], $floorsInfo);
                $HTML .= '</div></div>';
            }
        }
        return $HTML;
    }
    
    public function updateVote($args = ['id' => '', 'value' => ''])
    {
        $defaults = ['id' => '', 'value' => ''];
        $args = merge_args($defaults, $args);
        if
        (
            !ctype_digit((string)$args['id']) || 
            !in_array($args['value'], [1, -1])
        )
        {
            throw new \InvalidArgumentException("Invalid id or value.");
        }
        $comment = $this->getById(['id' => $args['id']]);
        if (empty($comment))
        {
            return [1, "ID为{$args['id']}的评论不存在", $args];
        }
        try
        {
            if ($args['value'] == 1)
            {
                $result = $this->updateById([
                    'id' => $args['id'],
                    'set' => 'like_count = like_count + 1',
                ]);
                $num = $comment['like_count'] + 1;
            }
            else
            {
                $result = $this->updateById([
                    'id' => $args['id'],
                    'set' => 'dislike_count = dislike_count + 1',
                ]);
                $num = $comment['dislike_count'] + 1;
            }
            return [0, "更新成功", ['num' => $num]];
        }
        catch (\Exception $e)
        {
            return [1, sprintf('%s in file: %s at line: %s', $e->getMessage(), $e->getFile(), $e->getLine()), $args];
        }
        
    }
    
    /**
     * 获取更多评论。
     * @param array $args 参数：render, 是否浸染, 默认true
     * @throws \InvalidArgumentException
     * @return array ['comments' => [], 'commentsParents' => []]
     */
    public function getMore($args = ['parent_comment_id' => '', 'offset' => '', 'size' => 10, 'render' => true])
    {
        $defaults = ['parent_comment_id' => '', 'offset' => '', 'size' => 10, 'render' => true];
        $args = merge_args($defaults, $args);
        if
        (
            !ctype_digit((string)$args['parent_comment_id']) || 
            !ctype_digit((string)$args['offset']) || 
            !ctype_digit((string)$args['size'])
        )
        {
            throw new \InvalidArgumentException("Invalid arguments: " . json_encode($args));
        }
        $comments = $this->select([
            'where' => 'root_id = :root_id',
            'bind' => [':root_id' => $args['parent_comment_id']],
            'order' => 'create_time ASC',
            'limit' => sprintf("%s, %s", $args['offset'], $args['size']),
        ]);
        if (empty($comments))
        {
            return ['comments' => [], 'commentsParents' => []];
        }
        $commentsParentsIdArr = array_column($comments, 'parent_id');
        $commentsParents = $this->select([
            'where' => sprintf("id IN (%s)", implode(',', array_unique($commentsParentsIdArr))),
            'order' => 'create_time ASC',
        ]);      
        $commentsParents = array_column($commentsParents, null, 'id');
        if (!$args['render'])
        {
            return ['comments' => $comments, 'commentsParents' => $commentsParents];
        }
        $out = [];
        foreach ($comments as $comment)
        {
            $out[] = $this->renderOnePureComment($comment, $commentsParents);
        }
        return ['comments' => $out, 'commentsParents' => $commentsParents];
    }
    
    /**
     * 渲染一个纯净的评论
     * @param array $comment
     * @return string
     */
    public function renderOnePureComment(array $comment, array $commentsParents)
    {
        $HTML = '';
        $HTML .= sprintf('<div class="media mt-3" data-id="%s"><img class="d-flex pr-3" src="/img/avatar.svg" alt="Generic placeholder image"><div class="media-body"><h6 class="mt-0">', $comment['id']);
        $HTML .= sprintf('<small class="comment-meta">%s</small>', $this->getNickname($comment));
        $HTML .= '<small class="comment-meta">回复</small>';
        $HTML .= sprintf('<small class="comment-meta">%s</small>', $this->getNickname($commentsParents[$comment['parent_id']]));
        $HTML .= sprintf('<small class="comment-meta">发表于[%s]</small>', date('Y-m-d H:i:s', $comment['create_time']));
        $HTML .= sprintf('</h6>%s', $comment['content']);
        $HTML .= '<p class="actions text-right">';
        $HTML .= sprintf('<span class="like vote">支持(%s)</span>', $comment['like_count']);
        $HTML .= sprintf('<span class="dislike vote">反对(%s)</span>', $comment['dislike_count']);
        $HTML .= '<span class="reply">回复</span></p>';
        $HTML .= '</div></div>';
        
        return $HTML;
    }
    
}
